package org.example.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 切入点表达式：描述切入点方法的一种表达式
 * - 作用：主要用来决定项目中的哪些方法需要加入通知
 * - 常见形式：
 *   - execution(……)：根据方法的签名来匹配
 *   - @annotation(……) ：根据注解匹配
 ------------------------------------------
 * execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配，语法为：
 * execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)
 * 其中带?的表示可以省略的部分
 * - 访问修饰符：可省略（比如: public、protected）
 * - 包名.类名： 可省略
 * - throws 异常：可省略（注意是方法上声明抛出的异常，不是实际抛出的异常)
 -------------------------------------------------------------
 * 可以使用通配符描述切入点
 * - * ：单个独立的任意符号，可以通配任意返回值、包名、类名、方法名、任意类型的一个参数，也可以通配包、类、方法名的一部分
 * - .. ：多个连续的任意符号，可以通配任意层级的包，或任意类型、任意个数的参数
 * 切入点表达式的语法规则：
 * 1. 方法的访问修饰符可以省略
 * 2. 返回值可以使用*号代替（任意返回值类型）
 * 3. 包名可以使用*号代替，代表任意包（一层包使用一个*）
 * 4. 使用..配置包名，标识此包以及此包下的所有子包
 * 5. 类名可以使用*号代替，标识任意类
 * 6. 方法名可以使用*号代替，表示任意方法
 * 7. 可以使用 *  配置参数，一个任意类型的参数
 * 8. 可以使用.. 配置参数，任意个任意类型的参数
 -----------------------------------------
 * 切入点表达式书写建议：
 * - 所有业务方法名在命名时尽量规范，方便切入点表达式快速匹配。如：findXxx，updateXxx。
 * - 描述切入点方法通常基于接口描述，而不是直接描述实现类，增强拓展性。
 * - 在满足业务需要的前提下，尽量缩小切入点的匹配范围。如：包名尽量不使用..，使用 * 匹配单个包。
 */
//@Aspect
@Slf4j
@Component
public class MyAspect3 {
    //- 省略方法的修饰符号
    @Before("execution(void org.example.service.impl.DeptServiceImpl.deleteById(java.lang.Integer))")
    public void before(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用*代替返回值类型
    @Before("execution(* org.example.service.impl.DeptServiceImpl.deleteById(java.lang.Integer))")
    public void before1(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用*代替包名（一层包使用一个*）
    @Before("execution(* org.example.*.*.DeptServiceImpl.deleteByIdById(java.lang.Integer))")
    public void before2(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用..省略包名
    @Before("execution(* org..DeptServiceImpl.deleteByIdById(java.lang.Integer))  ")
    public void before3(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用*代替类名
    @Before("execution(* org..*.deleteByIdById(java.lang.Integer))")
    public void before4(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用*代替方法名
    @Before("execution(* org..*.*(java.lang.Integer))")
    public void before5(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用 * 代替参数
    @Before("execution(* org.example.service.impl.DeptServiceImpl.deleteById(*))")
    public void before6(){
        log.info("MyAspect3 -> before ...");
    }
    //- 使用..省略参数
    @Before("execution(* org..*.*(..))")
    public void before7(){
        log.info("MyAspect3 -> before ...");
    }
    //根据业务需要，可以使用 且（&&）、或（||）、非（!） 来组合比较复杂的切入点表达式
    @Before("execution(* org.example.service.DeptService.findAll(..)) || execution(* org.example.service.DeptService.deleteById(..))")
    public void before8(){
        log.info("MyAspect3 -> before ...");
    }
    //- //匹配DeptServiceImpl类中以find开头的方法
    @Before("execution(* org.example.service.impl.DeptServiceImpl.find*(..))")
    public void before9(){
        log.info("MyAspect3 -> before ...");
    }
    //- 描述切入点方法通常基于接口描述，而不是直接描述实现类，增强拓展性
    @Before("execution(* org.example.service.DeptService.*(..))")
    public void before10(){
        log.info("MyAspect3 -> before ...");
    }
    //- 在满足业务需要的前提下，尽量缩小切入点的匹配范围。如：包名匹配尽量不使用 ..，使用 * 匹配单个包
    @Before("execution(* org.example.*.*.DeptServiceImpl.find*(..))")
    public void before11(){
        log.info("MyAspect3 -> before ...");
    }
}
